--- /dev/null
+<chapter id="gtk-migrating-GtkAction">
+ <chapterinfo>
+ <author>
+ <firstname>Federico</firstname>
+ <surname>Mena-Quintero</surname>
+ <affiliation>
+ <address>
+ <email>federico@ximian.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </chapterinfo>
+
+ <title>Migrating from old menu and toolbar systems to GtkAction</title>
+
+ <para>
+ Prior to GTK+ 2.4, there were several APIs in use to create menus
+ and toolbars. GTK+ itself included #GtkItemFactory, which was
+ historically used in the GIMP; libgnomeui provided the gnome-ui
+ set of macros; libbonoboui provided a complex mechanism to do menu
+ merging across embedded components. GTK+ 2.4 includes a system
+ for creating menus and toolbars, with merging of items, based
+ around the #GtkAction mechanism.
+ </para>
+
+ <section id="actions-and-action-groups">
+ <title>Actions and Action Groups</title>
+
+ <para>
+ A #GtkAction represents an operation that the user can perform
+ from the menus and toolbars of an application. It is similar to
+ "verbs" in other menu systems. A #GtkAction has a name, which
+ is its identifier, and it can have several widgets that
+ represent it in the user interface. For example, an action for
+ <symbol>EditCopy</symbol> can have a menu item as well as a
+ toolbar button associated to it. If there is nothing selected
+ in the document, the application can simply de-sensitize the
+ <symbol>EditCopy</symbol> action; this will cause both the menu
+ item and the toolbar button to be de-sensitized automatically.
+ Similarly, whenever the user selects the menu item or the
+ toolbar button associated to the <symbol>EditCopy</symbol>
+ action, the corresponding #GtkAction object will emit an
+ "activate" signal.
+ </para>
+
+ <para>
+ #GtkActionGroup is simply a group of #GtkAction objects. An
+ application may want to have several groups: one for global
+ actions such as "new document", "about", and "exit"; then one
+ group for each open document with actions specific to the
+ document, such as "cut", "copy", "paste", and "print".
+ </para>
+
+ <para>
+ Normal actions are simply commands, such as
+ <symbol>FileSave</symbol> or <symbol>EditCopy</symbol>. Toggle
+ actions can be active or inactive, such as
+ <symbol>FormatBold</symbol> or <symbol>ViewShowRulers</symbol>.
+ Radio actions define a set of items for which one and only one
+ can be active at a time, for example, {
+ <symbol>ViewHighQuality</symbol>,
+ <symbol>ViewNormalQuality</symbol>,
+ <symbol>ViewLowQuality</symbol> }.
+ </para>
+ </section>
+
+ <section id="ui-manager">
+ <title>User Interface Manager Object</title>
+
+ <para>
+ #GtkUIManager is an object that can construct menu and toolbar
+ widgets from an XML description. These widgets are in turn
+ associated to corresponding actions and action groups.
+ </para>
+
+ <para>
+ #GtkUIManager supports merging of menus and toolbars for
+ applications that have multiple components, each with separate
+ sets of commands. For example, a word processor that can embed
+ images may want to have toolbar buttons for Bold and Italic when
+ the cursor is on a text block, but Crop and Brightness/Contrast
+ buttons when the cursor is on an image. These actions, which
+ change depending on the state of the application, can be merged
+ and de-merged from a #GtkUIManager as appropriate.
+ </para>
+ </section>
+
+ <section id="migrating-gnomeuiinfo">
+ <title>Migrating from GnomeUIInfo</title>
+
+ <para>
+ Prior to GTK+ 2.4, some applications used the GnomeUIInfo
+ mechanism from
+ <filename><libgnomeui/gnome-app-helper.h></filename> to
+ define their menus and toolbars. With it, a program decleres an
+ array of <structname>GnomeUIInfo</structname> structures, which
+ contain information for menu or toolbar items such as their
+ label, icon, and accelerator key. Then, one calls
+ <function>gnome_app_fill_menu()</function> or
+ <function>gnome_app_fill_toolbar()</function>, or one of the
+ related functions, to create the appropriate widgets based on
+ these structures.
+ </para>
+
+ <para>
+ A downside of this API is that the same structures are used to
+ pass back pointers to the widgets that got created. This means
+ that the structures cannot simply be kept around if the program
+ requires multiple instances of the user interface (e.g. several
+ windows); each new invocation of
+ <function>gnome_app_fill_menu()</function> would overwrite the
+ widget fields of the structures.
+ </para>
+
+ <para>
+ Another disadvantage is that there is no automatic way to
+ synchronize the state of related controls. If there are toolbar
+ toogle buttons for "Bold", "Italic", "Underline", and also
+ corresponding menu items under "Format/Bold", etc., one has to
+ synchronize their toggled states by hand whenever the user
+ selects any one of them.
+ </para>
+
+ <para>
+ Finally, there is no way to do menu and toolbar merging for
+ applications that require embedded components.
+ </para>
+
+ <para>
+ To convert an application that uses GnomeUIInfo into the new
+ GtkAction mechanism, you need to do several things:
+ </para>
+
+ <orderedlist>
+ <listitem>
+ <para>
+ Separate your existing GnomeUIInfo entries into normal
+ actions, toggle actions, and radio actions, and then create a
+ separate array of #GtkActionEntry structures for each
+ group. This will allow you to create the necessary
+ #GtkActionGroup objects. Note that this does not describe
+ the actual "shape" that your menus and toolbars will have;
+ it simply defines the set of commands that will appear in them.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Create an XML description of your menus and toolbars for use
+ with #GtkUIManager. This defines the actual shape of the
+ menus and toolbars.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Port the code that uses gnome-app and gnome-app-helper to
+ #GtkAction and #GtkUIManager.
+ </para>
+ </listitem>
+ </orderedlist>
+
+ <example id="gnomeuiinfo-example">
+ <title>GnomeUIInfo Example</title>
+
+ <para>
+ The following code shows a declaration of a simple menu bar to
+ be used with <function>gnome_app_fill_menu()</function> or
+ similar. The menu hierarchy looks like this:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para><guimenu>File</guimenu></para>
+ <simplelist>
+ <member><guimenuitem>Open</guimenuitem></member>
+ <member><guimenuitem>—</guimenuitem></member>
+ <member><guimenuitem>Exit</guimenuitem></member>
+ </simplelist>
+ </listitem>
+
+ <listitem>
+ <para><guimenu>View</guimenu></para>
+ <simplelist>
+ <member><guimenuitem>Zoom In</guimenuitem></member>
+ <member><guimenuitem>Zoom Out</guimenuitem></member>
+ <member><guimenuitem>—</guimenuitem></member>
+ <member><guimenuitem>[ ] Full Screen</guimenuitem></member>
+ <member><guimenuitem>—</guimenuitem></member>
+ <member><guimenuitem>( ) High Quality</guimenuitem></member>
+ <member><guimenuitem>( ) Normal Quality</guimenuitem></member>
+ <member><guimenuitem>( ) Low Quality</guimenuitem></member>
+ </simplelist>
+ </listitem>
+ </itemizedlist>
+
+ <programlisting>
+static GnomeUIInfo file_menu_items[] = {
+ { GNOME_APP_UI_ITEM, "_Open", "Open a file",
+ open_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_OPEN,
+ 'o', GDK_CONTROL_MASK, NULL },
+ { GNOME_APP_UI_SEPARATOR },
+ { GNOME_APP_UI_ITEM, "E_xit", "Exit the program",
+ exit_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_QUIT,
+ 'q', GDK_CONTROL_MASK, NULL},
+ { GNOME_APP_UI_ENDOFINFO }
+};
+
+static GnomeUIInfo view_radio_items[] = {
+ { GNOME_APP_UI_ITEM, "_High Quality", "Display images in high quality, slow mode",
+ high_quality_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
+ 0, 0, NULL },
+ { GNOME_APP_UI_ITEM, "_Normal Quality", "Display images in normal quality",
+ normal_quality_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
+ 0, 0, NULL },
+ { GNOME_APP_UI_ITEM, "_Low Quality", "Display images in low quality, fast mode",
+ low_quality_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
+ 0, 0, NULL },
+ { GNOME_APP_UI_ENDOFINFO }
+};
+
+static GnomeUIInfo view_menu_items[] = {
+ { GNOME_APP_UI_ITEM, "Zoom _In", "Zoom into the image",
+ zoom_in_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_ZOOM_IN,
+ GDK_PLUS, 0, NULL },
+ { GNOME_APP_UI_ITEM, "Zoom _Out", "Zoom away from the image",
+ zoom_out_callback, NULL, NULL, GNOME_APP_PIXMAP_STOCK, GTK_STOCK_ZOOM_OUT,
+ GDK_MINUS, 0, NULL },
+ { GNOME_APP_UI_SEPARATOR },
+ { GNOME_APP_UI_TOGGLEITEM, "_Full Screen", "Switch between full screen and windowed mode",
+ full_screen_callback, NULL, NULL, GNOME_APP_PIXMAP_NONE, NULL,
+ GDK_F11, 0, NULL },
+ { GNOME_APP_UI_SEPARATOR },
+ { GNOME_APP_UI_RADIOITEMS, NULL, NULL, view_radio_items },
+ { GNOME_APP_UI_ENDOFINFO }
+};
+
+static GnomeUIInfo menubar[] = {
+ { GNOME_APP_UI_SUBTREE, "_File", NULL, file_menu_items },
+ { GNOME_APP_UI_SUBTREE, "_View", NULL, view_menu_items },
+ { GNOME_APP_UI_ENDOFINFO }
+}
+ </programlisting>
+ </example>
+
+ <example id="gnomeuiinfo-action-entries">
+ <title><structname>GtkActionEntry</structname> Structures</title>
+
+ <para>
+ The following code is the set of actions that are present in
+ the <link linkend="gnomeuiinfo-example">previous
+ example</link>. Note that the toggle and radio entries are
+ separate from normal actions. Also, note that #GtkActionEntry
+ structures take key names in the format of
+ gdk_accelerator_parse() rather than key values plus modifiers;
+ you will have to convert these values by hand. For example,
+ <constant>GDK_F11</constant> with no modifiers is equivalent
+ to a key name of <literal>"F11"</literal>. Likewise,
+ <literal>"o"</literal> with
+ <constant>GDK_CONTROL_MASK</constant> is equivalent to
+ <literal>"<ontrol>O"</literal>.
+ </para>
+
+ <programlisting>
+/* Normal items */
+static GtkActionEntry entries[] = {
+ { "FileMenu", NULL, "_File" },
+ { "ViewMenu", NULL, "_View" },
+ { "Open", GTK_STOCK_OPEN, "_Open", "<control>O", "Open a file", open_action_callback },
+ { "Exit", GTK_STOCK_OPEN, "E_xit", "<control>Q", "Exit the program", exit_action_callback },
+ { "ZoomIn", GTK_STOCK_ZOOM_IN, "Zoom _In", "plus", "Zoom into the image", zoom_in_action_callback },
+ { "ZoomOut", GTK_STOCK_ZOOM_OUT, "Zoom _Out", "minus", "Zoom away from the image", zoom_out_action_callback },
+};
+
+/* Toggle items */
+static GtkToggleActionEntry toggle_entries[] = {
+ { "FullScreen", NULL, "_Full Screen", "F11", "Switch between full screen and windowed mode", full_screen_action_callback, FALSE }
+};
+
+/* Radio items */
+static GtkRadioActionEntry radio_entries[] = {
+ { "HighQuality", NULL, "_High Quality", NULL, "Display images in high quality, slow mode", 0 },
+ { "NormalQuality", NULL, "_Normal Quality", NULL, "Display images in normal quality", 1 },
+ { "LowQuality", NULL, "_Low Quality", NULL, "Display images in low quality, fast mode", 2 }
+};
+ </programlisting>
+ </example>
+
+ <example id="gnomeuiinfo-xml">
+ <title>XML Description</title>
+
+ <para>
+ After extracting the actions, you will need to create an XML
+ description of the actual layout of your menus and toolbars
+ for use with #GtkUIManager. The following code shows a simple
+ menu bar that corresponds to the <link
+ linkend="gnomeuiinfo-example">previous example</link>. Note
+ that the <guimenu>File</guimenu> and <guimenu>View</guimenu>
+ menus have their names specified in the <link
+ linkend="gnomeuiinfo-action-entries">action entries</link>,
+ not in the XML itself. This is because the XML description
+ only contains <emphasis>identifiers</emphasis> for the items
+ in the GUI, rather than human-readable names.
+ </para>
+
+ <programlisting>
+static const char *ui_description =
+"<ui>"
+" <menubar name="MainMenu">"
+" <menu action="FileMenu">"
+" <menuitem action="Open"/>"
+" <menuitem action="Exit"/>"
+" </menu>"
+" <menu action="ViewMenu">"
+" <menuitem action="ZoomIn"/>"
+" <menuitem action="ZoomOut"/>"
+" <separator/>"
+" <menuitem action="FullScreen"/>"
+" <separator/>"
+" <menuitem action="HighQuality"/>"
+" <menuitem action="NormalQuality"/>"
+" <menuitem action="LowQuality"/>"
+" </menu>"
+" </menubar>"
+"</ui>";
+ </programlisting>
+ </example>
+
+ <example id="gnomeuiinfo-code">
+ <title>Creating the Menu Bar</title>
+
+ <para>
+ In this last example, we will create a #GtkActionGroup based
+ on the <link linkend="gnomeuiinfo-action-entries">action
+ entries</link> we created above. We will then create a
+ #GtkUIManager with the <link linkend="gnomeuiinfo-xml">XML
+ description</link> of the menu layout. We will also extract
+ the accelerator group and the widgets from the #GtkUIManager
+ put them into a window.
+ </para>
+
+ <programlisting>
+GtkWidget *window;
+GtkWidget *vbox;
+GtkWidget *menubar;
+GtkActionGroup *action_group;
+GtkUIManager *ui_manager;
+GtkAccelGroup *accel_group;
+GError *error;
+
+window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+
+vbox = gtk_vbox_new (FALSE, 0);
+gtk_container_add (GTK_CONTAINER (window), vbox);
+
+action_group = gtk_action_group_new ("MenuActions");
+gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries), window);
+gtk_action_group_add_toggle_actions (action_group, toggle_entries, G_N_ELEMENTS (toggle_entries), window);
+gtk_action_group_add_radio_actions (action_group, radio_entries, G_N_ELEMENTS (radio_entries), 0, radio_action_callback, window);
+
+ui_manager = gtk_ui_manager_new ();
+gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
+
+accel_group = gtk_ui_manager_get_accel_group (ui_manager);
+gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
+
+error = NULL;
+if (!gtk_ui_manager_add_ui_from_string (ui_manager, ui_description, -1, &error))
+ {
+ g_message ("building menus failed: %s", error->message);
+ g_error_free (error);
+ exit (EXIT_FAILURE);
+ }
+
+menubar = gtk_ui_manager_get_widget ("/MainMenu");
+gtk_box_pack_start (GTK_BOX (vbox), menubar, FALSE, FALSE, 0);
+
+gtk_widget_show_all (window);
+ </programlisting>
+ </example>
+ </section>
+
+</chapter>
+
+<!--
+Local variables:
+mode: sgml
+sgml-parent-document: ("gtk-docs.sgml" "book" "part" "chapter")
+End:
+-->